﻿#include "mfcdialog_pch.h"

#include "mfcdialog_dlg.h"

#include "SpdlogSinkFactory.h"  // For creating the sink
#include "afxdialogex.h"
#include "logger\LogBroker.h"
#include "logger\LogManager.h"
#include "logger\LogListControl.h"
#include "logger\WindowSink.h"  // The sink for our control
#include "mfcdialog.h"
#include "mfcdialog_framework.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CMfcLogViewerDlg 对话框

CMfcLogViewerDlg::CMfcLogViewerDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_MFCAPPLICATION1_DIALOG, pParent),
      m_pLogListControl(nullptr)  // 初始化指针
{
  m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMfcLogViewerDlg::DoDataExchange(CDataExchange* pDX) {
  CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CMfcLogViewerDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_DESTROY()
ON_BN_CLICKED(IDC_BUTTON_GENERATE, &CMfcLogViewerDlg::OnBnClickedButtonGenerate)
ON_WM_SIZE()
ON_WM_GETMINMAXINFO()
END_MESSAGE_MAP()

// CMfcLogViewerDlg 消息处理程序

BOOL CMfcLogViewerDlg::OnInitDialog() {
  CDialogEx::OnInitDialog();

  // 设置此对话框的图标。  当应用程序主窗口不是对话框时，框架将自动
  //  执行此操作
  SetIcon(m_hIcon, TRUE);   // 设置大图标
  SetIcon(m_hIcon, FALSE);  // 设置小图标

  ShowWindow(SW_SHOW);

  // TODO: 在此添加额外的初始化代码
  // 1. 获取占位符控件的指针和位置
  CWnd* pPlaceholder = GetDlgItem(IDC_LOG_PLACEHOLDER);
  if (pPlaceholder) {
    CRect rect;
    pPlaceholder->GetWindowRect(&rect);
    ScreenToClient(&rect);

    // 2. 创建 CLogListControl 实例 (它会在 WM_CREATE 中自我配置)
    m_pLogListControl = new Logging::CLogListControl();
    //m_pLogListControl->SetPattern("[%Y/%m/%d %H:%M:%S.%e] [%p] [%c] [%t] %v");
    m_pLogListControl->SetPattern("[%c] %v");
    m_pLogListControl->SetTheme(
        Logging::CLogListControl::Theme::Light);
    // 3. 创建控件的窗口 (HWND)
    // 控件的 OnCreate 方法会自动创建并注册 WindowSink。
    HWND hWndLog = CreateWindowExW(
        0, Logging::GetLogListClass(), L"", WS_CHILD | WS_VISIBLE | WS_BORDER,
        rect.left, rect.top, rect.Width(), rect.Height(), this->GetSafeHwnd(),
        (HMENU)IDC_LOG_PLACEHOLDER, AfxGetInstanceHandle(),
        m_pLogListControl  // 将 CLogListControl 指针传递给 WM_CREATE
    );

    if (hWndLog) {
      // 4. (可选) 隐藏占位符
      // pPlaceholder->ShowWindow(SW_HIDE);
      pPlaceholder->DestroyWindow();

      // 5. (可选) 如果需要，可以设置特定类别
      // m_pLogListControl->SetLogCategory("MfcApp");
      // 默认情况下，它会监听所有类别 ("*")

      // 1. 获取对话框的初始客户区大小
      CRect rcClient;
      ::GetWindowRect(GetSafeHwnd(), rcClient);

      // 2. 获取 LogView 的初始位置 (相对于客户区)
      ::GetWindowRect(hWndLog, &m_rcLogView);
      ScreenToClient(&m_rcLogView);

      // 3. 获取 OK/CANCEL 按钮的初始位置
      GetDlgItem(IDOK)->GetWindowRect(&m_rcOkButton);
      ScreenToClient(&m_rcOkButton);

      GetDlgItem(IDCANCEL)->GetWindowRect(&m_rcCancelButton);
      ScreenToClient(&m_rcCancelButton);

      // 4. 获取 Generate 按钮的初始位置
      GetDlgItem(IDC_BUTTON_GENERATE)->GetWindowRect(&m_rcGenButton);
      ScreenToClient(&m_rcGenButton);

      m_initialSize.cx = m_rcLogView.Width() + 30;
      m_initialSize.cy = m_rcLogView.Height() + 60;

      // (可选) 设置最小尺寸，防止布局错乱
      OnGetMinMaxInfo(nullptr);  // 初始化最小尺寸
    } else {
      // 如果创建失败，清理资源
      delete m_pLogListControl;
      m_pLogListControl = nullptr;
    }
  }

  return TRUE;  // 除非将焦点设置到控件，否则返回 TRUE
}

// 如果向对话框添加最小化按钮，则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序，
//  这将由框架自动完成。

void CMfcLogViewerDlg::OnPaint() {
  if (IsIconic()) {
    CPaintDC dc(this);  // 用于绘制的设备上下文

    SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()),
                0);

    // 使图标在工作区矩形中居中
    int cxIcon = GetSystemMetrics(SM_CXICON);
    int cyIcon = GetSystemMetrics(SM_CYICON);
    CRect rect;
    GetClientRect(&rect);
    int x = (rect.Width() - cxIcon + 1) / 2;
    int y = (rect.Height() - cyIcon + 1) / 2;

    // 绘制图标
    dc.DrawIcon(x, y, m_hIcon);
  } else {
    CDialogEx::OnPaint();
  }
}

// 当用户拖动最小化窗口时系统调用此函数取得光标
// 显示。
HCURSOR CMfcLogViewerDlg::OnQueryDragIcon() {
  return static_cast<HCURSOR>(m_hIcon);
}

void CMfcLogViewerDlg::OnDestroy() {
  CDialogEx::OnDestroy();

  //  正确的做法
  // 虽然子窗口会被自动销毁，但显式调用 DestroyWindow 更清晰，
  // 并且能确保在对话框其他成员被销毁前，子控件的清理逻辑已经执行。
  // 但实际上，即使不写这行，当父对话框销毁时，Windows也会自动为我们
  // 销毁子窗口，从而触发我们的 WM_NCDESTROY 逻辑。
  // 所以，最关键的改动是移除 delete 语句。
  if (m_pLogListControl && ::IsWindow(m_pLogListControl->GetHwnd())) {
    // ::DestroyWindow(m_pLogListControl->GetHwnd()); //
    // 这行是可选的，但良好实践
  }
  m_pLogListControl = nullptr;  // 将指针置空，防止意外使用
}

void CMfcLogViewerDlg::OnBnClickedButtonGenerate() {
  // 获取一个 logger
  auto logger = Logging::LogManager::get_logger("MfcApp");

  // 生成不同级别的日志
  logger->info("This is an informational message from MFC.");
  logger->warn("This is a warning message.");
  logger->error("This is an error message!");
}

void CMfcLogViewerDlg::OnSize(UINT nType, int cx, int cy) {
  CDialogEx::OnSize(nType, cx, cy);

  // 如果控件尚未初始化完成或窗口被最小化，则不执行任何操作
  if (m_initialSize.cx == 0 || nType == SIZE_MINIMIZED) {
    return;
  }

  //  重新计算并应用布局

  // 1. 布局 CLogListControl (填充)
  // 它将保持左、上边距不变，但会拉伸到新的右、下边框 (保持右、下边距不变)
  if (m_pLogListControl &&
      ::IsWindow(m_pLogListControl->GetHwnd()))  // 确保窗口句柄有效
  {
    CRect newRcLogView;
    newRcLogView.left = m_rcLogView.left;
    newRcLogView.top = m_rcLogView.top;
    newRcLogView.right = cx - m_initialSize.cx + m_rcLogView.right;
    newRcLogView.bottom = cy - m_initialSize.cy + m_rcLogView.bottom;
    ::MoveWindow(m_pLogListControl->GetHwnd(), newRcLogView.left,
                 newRcLogView.top, newRcLogView.Width(), newRcLogView.Height(),
                 TRUE);
  }

  // 2. 布局 OK 按钮 (锚定在右下角)
  CWnd* pOkButton = GetDlgItem(IDOK);
  CRect rcOkButton = m_rcOkButton;
  if (pOkButton) {
    CRect newRcOk;
    newRcOk.right = cx - (m_initialSize.cx - m_rcOkButton.right);
    newRcOk.bottom = cy - (m_initialSize.cy - m_rcOkButton.bottom);
    newRcOk.left = newRcOk.right - m_rcOkButton.Width();
    newRcOk.top = newRcOk.bottom - m_rcOkButton.Height();
    pOkButton->MoveWindow(&newRcOk);
    rcOkButton = newRcOk;
  }

  CWnd* pCancelButton = GetDlgItem(IDCANCEL);
  if (pCancelButton) {
    CRect newRcct;
    newRcct.right = cx - (m_initialSize.cx - m_rcCancelButton.right);
    newRcct.bottom = cy - (m_initialSize.cy - m_rcCancelButton.bottom);
    newRcct.left = newRcct.right - m_rcCancelButton.Width();
    newRcct.top = newRcct.bottom - m_rcCancelButton.Height();
    pCancelButton->MoveWindow(&newRcct);
  }

  // 3. 布局 Generate 按钮 (锚定在 OK 按钮的左边)
  CWnd* pGenButton = GetDlgItem(IDC_BUTTON_GENERATE);
  if (pGenButton) {
    CRect newRcGen;
    // 它的右边框是 OK 按钮的左边框减去它们之间的原始间距
    int spacing = rcOkButton.left - m_rcGenButton.right;
    newRcGen.right = (pOkButton ? rcOkButton.left : cx) -
                     spacing;  // 如果OK按钮存在，则相对定位
    newRcGen.right = rcOkButton.left - spacing;

    // 更简单的做法是也让它锚定在右下角
    newRcGen.right = cx - (m_initialSize.cx - m_rcGenButton.right);
    newRcGen.bottom = cy - (m_initialSize.cy - m_rcGenButton.bottom);
    newRcGen.left = newRcGen.right - m_rcGenButton.Width();
    newRcGen.top = newRcGen.bottom - m_rcGenButton.Height();
    pGenButton->MoveWindow(&newRcGen);
  }
  // 强制重绘，以避免出现残影
  Invalidate();
}

void CMfcLogViewerDlg::OnGetMinMaxInfo(MINMAXINFO* lpMMI) {
  if (lpMMI == nullptr) {
    return;
  }

  if (m_initialSize.cx > 0) {
    // 你可以设置一个合理的最小尺寸
    // 例如，不小于初始大小的80%
    lpMMI->ptMinTrackSize.x = m_initialSize.cx * 0.8;
    lpMMI->ptMinTrackSize.y = m_initialSize.cy * 0.8;
  } else {
    // 如果在 OnInitDialog 之前被调用，可以设置一个硬编码的最小值
    lpMMI->ptMinTrackSize.x = 400;
    lpMMI->ptMinTrackSize.y = 300;
  }

  CDialogEx::OnGetMinMaxInfo(lpMMI);
}